package com.wjk.kylin.id.core.spring.boot.autoconfigure.uid;

import com.wjk.kylin.id.core.spring.boot.autoconfigure.KylinIdProperties;
import com.wjk.kylin.id.core.uid.impl.CachedUidGenerator;
import com.wjk.kylin.id.core.uid.impl.DefaultUidGenerator;
import com.wjk.kylin.id.core.worker.DefaultWorkerIdAssigner;
import com.wjk.kylin.id.core.worker.WorkerIdAssigner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * @author wjk
 */
@Configuration
@EnableConfigurationProperties({UIdProperties.class, KylinIdProperties.class})
@ConditionalOnProperty(name = "kylin.id.uid.enable", havingValue = "true")
public class UIdAutoConfiguration {
    private static final Logger LOGGER = LoggerFactory.getLogger(UIdAutoConfiguration.class);

    @Autowired
    private UIdProperties properties;
    @Autowired
    private KylinIdProperties kylinIdProperties;

    /**
     * 默认生成worker id 方案
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(WorkerIdAssigner.class)
    public WorkerIdAssigner workerIdAssigner() {
        //方案二：
        LOGGER.info(">>>>>>>>>>> kylin-uid-worker id init default");
        return new DefaultWorkerIdAssigner(properties.getBizType(), kylinIdProperties.getServer());
    }


    /**
     * 先加载上面这个
     * <p>
     * CachedUidGenerator
     * CachedUidGenerator是UidGenerator的重要改进实现
     * <p>
     * 参数	说明
     * boostPower	RingBuffer size扩容参数, 可提高UID生成的吞吐量默认:3， 原bufferSize=8192, 扩容后bufferSize= 8192 << 3 = 65536
     * paddingFactor	指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50举例: bufferSize=1024, paddingFactor=50 -> threshold=1024 * 50 / 100 = 512.当环上可用UID数量 < 512时, 将自动对RingBuffer进行填充补全
     * scheduleInterval	另外一种RingBuffer填充时机, 在Schedule线程中, 周期性检查填充默认:不配置此项, 即不实用Schedule线程. 如需使用, 请指定Schedule线程时间间隔, 单位:秒
     *
     * @param workerIdAssigner
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(CachedUidGenerator.class)
    public CachedUidGenerator cachedUidGenerator(WorkerIdAssigner workerIdAssigner) {

        CachedUidGenerator cachedUidGenerator = new CachedUidGenerator();
        cachedUidGenerator.setWorkerIdAssigner(workerIdAssigner);
        cachedUidGenerator.setTimeBits(properties.getTimeBits()); //136年
        cachedUidGenerator.setWorkerBits(properties.getWorkerBits());//420万次 100次/天 115年
        cachedUidGenerator.setSeqBits(properties.getSeqBits());//512/s并发
        cachedUidGenerator.setEpochStr(properties.getEpochStr());
        cachedUidGenerator.setBoostPower(properties.getCachedUidGenerator().getBoostPower());
        if (properties.getCachedUidGenerator().getPaddingFactor() > 0) {
            cachedUidGenerator.setPaddingFactor(properties.getCachedUidGenerator().getPaddingFactor());
        }
        if (properties.getCachedUidGenerator().getScheduleInterval() > 0) {
            cachedUidGenerator.setScheduleInterval(properties.getCachedUidGenerator().getScheduleInterval());
        }
        LOGGER.info(">>>>>>>>>>> kylin-uid-cache init properties:{}", properties);
        return cachedUidGenerator;
    }

    /**
     * DefaultUidGenerator是UidGenerator 默认的实现方式
     * <p>
     * 参数	说明
     * timeBits	相对于时间基点"2016-05-20"的增量值，单位：秒，可使用的时间为2^timeBis 秒例如：timeBits=30，则可使用230秒，约34年，timeBits=31，则可使用231秒，约68年
     * workerBits	机器id，最多可支持2^22约420w次机器启动。内置实现为在启动时由数据库分配，默认分配策略为用后即弃，每次启动都会重新生成一批ID，因此重启次数也是会有限制的，后续可提供复用策略。
     * seqBits	每秒下的并发序列，9 bits可支持每台服务器每秒512个并发。
     * epochStr	指集成UidGenerator生成分布式ID服务第一次上线的时间，可配置，也一定要根据你的上线时间进行配置，因为默认的epoch时间可是2016-09-20，不配置的话，会浪费好几年的可用时间。
     *
     * @param workerIdAssigner
     * @return
     */
    @Bean
    @ConditionalOnMissingBean({DefaultUidGenerator.class, CachedUidGenerator.class})
    public DefaultUidGenerator defaultUidGenerator(WorkerIdAssigner workerIdAssigner) {

        DefaultUidGenerator defaultUidGenerator = new DefaultUidGenerator();
        defaultUidGenerator.setWorkerIdAssigner(workerIdAssigner);
        defaultUidGenerator.setTimeBits(properties.getTimeBits()); //136年
        defaultUidGenerator.setWorkerBits(properties.getWorkerBits());//420万次
        defaultUidGenerator.setSeqBits(properties.getSeqBits());//512/s并发
        defaultUidGenerator.setEpochStr(properties.getEpochStr());

        LOGGER.info(">>>>>>>>>>> kylin-uid-default init properties:{}", properties);
        return defaultUidGenerator;
    }

    /**
     * 对于并发数要求不高、期望长期使用的应用, 可增加timeBits位数, 减少seqBits位数.
     * 例如节点采取用完即弃的WorkerIdAssigner策略, 重启频率为12次/天,
     * 那么配置成{"workerBits":23,"timeBits":31,"seqBits":9}时,
     * 可支持28个节点以整体并发量14400 UID/s的速度持续运行68年.
     *
     * 对于节点重启频率频繁、期望长期使用的应用, 可增加workerBits和timeBits位数, 减少seqBits位数.
     * 例如节点采取用完即弃的WorkerIdAssigner策略, 重启频率为24*12次/天,
     * 那么配置成{"workerBits":27,"timeBits":30,"seqBits":6}时,
     * 可支持37个节点以整体并发量2400 UID/s的速度持续运行34年.
     */
}

